#!/bin/bash

INIT_NAME="ainitramfs"

init_msg() {
    echo "${INIT_NAME}: $*" > /dev/console
}

init_error() {
    init_msg "Error: $*"
    init_msg "Boot failed, dropping to shell."
    exec /bin/bash
    init_msg "Failed to exec shell, sleeping forever."
    while true; do sleep 1; done
}

wait_for_device() {
    local spec="$1"
    local dev

    if [[ ${spec} =~ ^UUID=(.*)$ ]]; then
        dev="/dev/disk/by-uuid/${BASH_REMATCH[1]}"
    elif [[ ${spec} =~ ^LABEL=(.*)$ ]]; then
        dev="/dev/disk/by-id/${BASH_REMATCH[1]}"
    elif [[ ${spec} =~ ^PATH=(.*)$ ]]; then
        dev="/dev/disk/by-path/${BASH_REMATCH[1]}"
    elif [[ ${spec} =~ ^/dev/ ]]; then
        dev="${spec}"
    else
        init_error "Unknown device specification ( ${spec} )"
    fi

    while true; do
        if [[ -b ${dev} ]]; then
            echo "${dev}"
            break
        fi
        sleep 0.2
    done
}

find_udevd() {
    local paths="/usr/lib/systemd/systemd-udevd /lib/udev/udevd /sbin/udevd"
    for path in $paths; do
        if [[ -e $path ]]; then
            echo "$path"
            return 0
        fi
    done
    return 1
}

export PATH="/sbin:/bin:/usr/sbin:/usr/bin:/lib/udev"

init_msg "Mounting filesystems"
mount -n -t proc proc /proc || init_error "Cannot mount /proc"
mount -n -t sysfs sys /sys || init_error "Cannot mount /sys"
mount -n -t devtmpfs -o "exec,nosuid,mode=0755,size=10M" dev /dev ||
    mount -n -t tmpfs -o "exec,nosuid,mode=0755,size=10M" dev /dev ||
    init_error "Cannot mount /dev"
[ -e /dev/console ] || mknod -m 600 /dev/console c 5 1 || init_error "cannot mknod /dev/console"
[ -e /dev/null ] || mknod -m 666 /dev/null c 1 3 || init_error "cannot mknod /dev/null"
[ -e /run ] || mkdir /run || init_error "cannot mkdir /run"
mount -n -t tmpfs -o "rw,nosuid,nodev,relatime,mode=755" run /run || init_error "Cannot mount /run"

init_msg "Running depmod"
depmod || init_error "depmod failed"

udevd_path=$(find_udevd)
if [[ $? != 0 ]]; then
     init_error "cannot find udevd executable"
fi

init_msg "Starting udev"
"$udevd_path" --daemon --resolve-names=never || init_error "Cannot start udevd"
udevadm trigger || init_error "udevadm trigger failed"
udevadm settle || init_error "udevadm settle failed"

NET_SPEC=$(getkernelarg /proc/cmdline ai_net)
if [[ -n ${NET_SPEC} ]]; then
    [[ ${NET_SPEC} =~ ^([^:]*):([^:]*):([^:]*):([^:]*)$ ]] || init_error "cannot parse ai_net="
    NET_IF="${BASH_REMATCH[1]}"
    NET_IP="${BASH_REMATCH[2]}"
    NET_MASK="${BASH_REMATCH[3]}"
    NET_GW="${BASH_REMATCH[4]}"

    init_msg "Configuring network"

    ifconfig "${NET_IF}" "${NET_IP}" netmask "${NET_MASK}" || init_error "ifconfig failed"

    if [[ -n ${NET_GW} ]]; then
        route add default gw "${NET_GW}" || init_error "route failed"
    fi
fi

NBD_SPEC=$(getkernelarg /proc/cmdline ai_nbd)
if [[ -n $NBD_SPEC ]]; then
    [[ ${NBD_SPEC} =~ ^([^:]*):([^:]*)$ ]] || init_error "Bad ai_nbd argument."
    NBD_HOST="${BASH_REMATCH[1]}"
    NBD_PORT="${BASH_REMATCH[2]}"

    init_msg "Setting up NBD ( host=${NBD_HOST} port=${NBD_PORT} )"
    modprobe nbd max_part=32
    @nbd-client "${NBD_HOST}" "${NBD_PORT}" /dev/nbd0 -p || init_error "Cannot start nbd-client"
fi

CRYPT_SPEC=$(getkernelarg /proc/cmdline ai_crypt)
if [[ -n $CRYPT_SPEC ]]; then
    [[ ${CRYPT_SPEC} =~ ^([^:]*):(.*)$ ]] || init_error "Bad ai_crypt argument."
    CRYPT_NAME="${BASH_REMATCH[1]}"
    CRYPT_DEVICE="${BASH_REMATCH[2]}"

    init_msg "Waiting for crypt device"
    CRYPT_REAL_DEVICE=$(wait_for_device "${CRYPT_DEVICE}")

    init_msg "Setting up disk encryption ( device=${CRYPT_REAL_DEVICE} name=${CRYPT_NAME} )"
    cryptsetup luksOpen "${CRYPT_REAL_DEVICE}" "${CRYPT_NAME}" || init_error "Failed to run cryptsetup"
fi

RESUMESPEC=$(getkernelarg /proc/cmdline resume)
if [[ -n $RESUMESPEC ]]; then
    init_msg "Waiting for resume device"
    RESUMEDEV=$(wait_for_device "${RESUMESPEC}")
    if [[ -d /sys/power/tuxonice ]]; then
        init_msg "Trying tuxonice resume ( ${RESUMEDEV} )"
        echo "${RESUMEDEV}" > /sys/power/tuxonice/resume
        echo 1 > /sys/power/tuxonice/do_resume
    else
        init_msg "Trying resume ( ${RESUMEDEV} )"
        stat -L -c%t:%T "${RESUMEDEV}" > /sys/power/resume
    fi
fi

ROOTSPEC=$(getkernelarg /proc/cmdline root)
[[ -n $ROOTSPEC ]] || init_error "root= not given"

ROOTFLAGS=$(getkernelarg /proc/cmdline rootflags)

if [[ ${ROOTSPEC} =~ ^nfs:(.*)$ ]]; then
    NFS_PATH="${BASH_REMATCH[1]}"

    init_msg "Mounting NFS root ( ${NFS_PATH} )"
    mount -n -t nfs -o "ro,${ROOTFLAGS}" "${NFS_PATH}" /newroot || init_error "Cannot mount root"
else
    init_msg "Waiting for root device"
    ROOTDEV=$(wait_for_device "${ROOTSPEC}")

    init_msg "Mounting root ( ${ROOTDEV} )"
    mount -n -o "ro,${ROOTFLAGS}" "$ROOTDEV" /newroot || init_error "Cannot mount root"
fi

init_msg "Stopping udev"
udevadm control --exit >/dev/null 2>&1
if [[ $? != 0 ]]; then
    udevadm control --stop-exec-queue || init_error "Cannot udevadm control --stop-exec-queue"
    while pidof systemd-udevd udevd >/dev/null 2>&1; do
        for pid in $(pidof systemd-udevd udevd); do
            kill $pid >/dev/null 2>&1
        done
        sleep 0.1
    done
fi

init_msg "Switching root"

INIT=$(getkernelarg /proc/cmdline init)
[[ -z $INIT ]] && INIT="/sbin/init"

exec switch_root /newroot "$INIT"

init_error "Cannot switch root"
